"""
https://pytorch.org/docs/stable/tensors.html?highlight=tensor#torch.Tensor


torch.Tensor
There are a few main ways to create a tensor, depending on your use case.

To create a tensor with pre-existing data, use torch.tensor().

To create a tensor with specific size, use torch.* tensor creation ops (see Creation Ops).

To create a tensor with the same size (and similar types) as another tensor, use torch.*_like tensor creation ops (see Creation Ops).

To create a tensor with similar type but different size as another tensor, use tensor.new_* creation ops.
"""
from python_ai.common.xcommon import *
import numpy as np
import torch as pt
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
import sys
import os
from sklearn.preprocessing import StandardScaler

np.random.seed(777)
pt.manual_seed(777)

x = np.arange(24).astype(np.float64).reshape((2, 3, 4))
print('x', type(x), x)

xT = pt.Tensor(x)
print('xT', type(xT), xT)

"""
WARNING

torch.tensor() always copies data. If you have a Tensor data and want to avoid a copy, 
use torch.Tensor.requires_grad_() or torch.Tensor.detach(). If you have a NumPy ndarray and want to avoid a copy, 
use torch.as_tensor(). 

WARNING

When data is a tensor x, torch.tensor() reads out ‘the data’ from whatever it is passed, and constructs a leaf 
variable. Therefore torch.tensor(x) is equivalent to x.clone().detach() and torch.tensor(x, requires_grad=True) is 
equivalent to x.clone().detach().requires_grad_(True). The equivalents using clone() and detach() are recommended. """
xt = pt.tensor(x)
print('xt', type(xt), xt)

"""
Creates a Tensor from a numpy.ndarray.

The returned tensor and ndarray share the same memory. Modifications to the tensor will be reflected in the ndarray 
and vice versa. The returned tensor is not resizable.
"""
fnt = pt.from_numpy(x)
print('fnt', type(fnt), fnt)

"""
eturns a copy of input.

NOTE

This function is differentiable, so gradients will flow back from the result of this operation to input. To create a 
tensor without an autograd relationship to input see detach(). """
clonet = fnt.clone()
print('clonet', type(clonet), clonet)

sep('x[1, 0, 2] = 114.')
x[1, 0, 2] = 114.
print('x', type(x), x)
print('xT', type(xT), xT)
print('xt', type(xt), xt)
print('fnt', type(fnt), fnt)
print('clonet', type(clonet), clonet)

sep('transpose')
trans_x = x.transpose(1, 2, 0)
print('trans_x', type(trans_x), trans_x)

onet = pt.ones_like(xt)
print('onet', type(onet), onet)
